/* Software Name : AsmDex
 * Version : 1.0
 *
 * Copyright © 2012 France Télécom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.ow2.asmdex.structureWriter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.ow2.asmdex.Opcodes;
import org.ow2.asmdex.instruction.Instruction;
import org.ow2.asmdex.lowLevelUtils.ByteVector;
import org.ow2.asmdex.lowLevelUtils.DalvikValueReader;
import org.ow2.asmdex.lowLevelUtils.IDalvikValueReader;
import org.ow2.asmdex.structureCommon.Label;
import org.ow2.asmdex.structureCommon.LocalVariable;

/**
 * Class representing the code instructions and debug informations of one method.
 * The instructions can link to debug instructions (the ones that don't concern lines/PC management).
 * Debug line/PC advances are managed directly by this class.
 * 
 * @author Julien Névo
 */
public class CodeItem {

	/**
	 * The generated code_item code. Includes the bytecode as well as the header of the code_item, 
	 * minus the reference to the debug_info_item as we don't know yet its offset.
	 * <i>CAN</i> include the padding at the end needed by the Try/catch. 
	 * It is generated through the generateBytecode method.
	 * It uses <i>symbolic</i> references, so must be parsed again to link them to the "real"
	 * elements.
	 */
	private ByteVector codeItemCode;

	/**
	 * The generated code_item try/catch section, just after the instructions, which should be stuck
	 * right after the codeItemCode. <i>Does NOT</i> include the padding right at the beginning. This ByteVector
	 * had to be separated from the code because it is composed of ULeb128 fields, which can change size.
	 * Instead of copying all the code if that happens, it is better to work on this part only.
	 */
	private ByteVector codeItemTryCatch;

	/**
	 * Offset to the tries_size field.
	 */
	public static final int TRIES_SIZE_FIELD_OFFSET = 6;

	/**
	 * Size in bytes of a try_item structure.
	 */
	private static final int TRY_ITEM_STRUCTURE_SIZE = 8;

	/**
	 * Offset to the debug_info_offset field.
	 */
	private static final int DEBUG_INFO_OFFSET_OFFSET = 4 * 2;

	/**
	 * Offset in bytes of the handler in a try_item structure.
	 */
	private static final int HANDLER_OFFSET_IN_TRY_ITEM_STRUCTURE = 6;

	/**
	 * Size in bytes of the header of the code_item structure (till insns_size included).
	 */
	public static final int HEADER_SIZE = 4 * 2 + 2 * 4;

	/**
	 * Offset in bytes of the insns_size structure.
	 */
	public static final int INSNS_SIZE_OFFSET = 12;

	/**
	 * The offset of the Code Item from the beginning of the Dex file. It is only known when producing the
	 * output file, after the input has been fully parsed.
	 */
	private int offset;

	/**
	 * Current size in bytes of the total instructions of this item (and by extension, the method).
	 * Does NOT take in account the header of the Code Item.
	 */
	private int size = 0;

	/**
	 * List of the Labels for this method.
	 */
	private List<Label> labels = new ArrayList<Label>();

	/**
	 * The number of words of incoming arguments used by this code.
	 */
	private int incomingArgumentsSizeInWord = 0;

	/**
	 * The number of words of outgoing arguments used by this code.
	 */
	private int outgoingArgumentsSizeInWord = 0;

	/**
	 * The number of words used by the register of the frame of this code.
	 * (= temporary registers + "this" (if non-static and non-constructor) + parameters).
	 */
	private int registerSize = 0;

	/**
	 * The first line number found. It may be 0 if none were found.
	 */
	private int firstLineNumber = 0;

	/**
	 * List of the Try/Catch of this Code Item.
	 */
	private ArrayList<TryCatch> tryCatches = new ArrayList<TryCatch>();

	/**
	 * List of the instructions of this code item.
	 */
	private ArrayList<Instruction> instructions = new ArrayList<Instruction>();

	/**
	 * The Method this Code Item belongs to.
	 */
	private Method method;

	/**
	 * The Debug Info Item of this Code Item.
	 */
	private DebugInfoItem debugInfoItem;

	/**
	 * The Constant Pool of the Application.
	 */
	final private ConstantPool constantPool;

	/**
	 * Sets of encoded_catch_handlers, so that they are unique.
	 */
	private HashSet<EncodedCatchHandler> encodedCatchHandlers = new HashSet<EncodedCatchHandler>();

	/**
	 * Map linking a TryCatch structure to a unique encoded_catch_handlers.
	 */
	private HashMap<TryCatch, EncodedCatchHandler> tryCatchToEncodedCatchHandler = new HashMap<TryCatch, EncodedCatchHandler>();

	/**
	 * Map linking a unique EncodedCatchHandler to its offset relative to the encoded_catch_handler_list,
	 * that is from where the try_items need it.
	 */
	private HashMap<EncodedCatchHandler, Integer> encodedCatchHandlerToRelativeOffset = new HashMap<EncodedCatchHandler, Integer>();

	/**
	 * Constructor of the Code Item.
	 * @param method the Method this Code Item belongs to.
	 */
	public CodeItem(Method method, ConstantPool constantPool) {
		this.method = method;
		this.constantPool = constantPool;

		debugInfoItem = new DebugInfoItem(constantPool);
	}


	// ------------------------------------
	// Public methods.
	// ------------------------------------

	/**
	 * Frees all the structures (list of instructions, try/catch...) of this element and the debug_info_item
	 * so that they don't consume memory. This <i>MUST</i> be done after having generated the bytecode, once
	 * the method has been parsed and its end visited.
	 */
	public void free() {
		labels = null;
		instructions = null;
		encodedCatchHandlers = null;
		tryCatchToEncodedCatchHandler = null;
		encodedCatchHandlerToRelativeOffset = null;

		debugInfoItem.free();
	}

	/**
	 * Adds a label to the set of used labels. If Null, nothing is done.
	 * @param label the label to add.
	 */
	public void addLabel(Label label) {
		if ((label != null) && label.isResolved()) {
			// We must be sure the Label isn't already in the list.
			// The Object must not be here, not is content (that's why "contains" isn't used).
			boolean found = false;
			Iterator<Label> it = labels.iterator();
			while (!found && it.hasNext()) {
				found = (it.next() == label);
			}

			if (!found) {
				labels.add(label);
			}
		}
	}

	/**
	 * Adds an instruction to the code item.
	 * @param instruction instruction to add to the code item.
	 */
	public void addInstruction(Instruction instruction) {
		instructions.add(instruction);
		size += instruction.getSize();
	}

	/**
	 * Adds a Try/Catch structure to the list of Try/Catch.
	 * @param tryCatch Try/Catch structure to add.
	 */
	public void addTryCatch(TryCatch tryCatch) {
		// First we need to know if a similar Try (same Start) already exists.
		// If yes, we add the Handler and Type to the Try.
		// Else, we add the structure directly.

		TryCatch foundTc = null;
		Iterator<TryCatch> iterator = tryCatches.iterator();
		while ((foundTc == null) && (iterator.hasNext())) {
			TryCatch tc = iterator.next();
			if (tc.getStart() == tryCatch.getStart()) {
				foundTc = tc;
			}
		}

		if (foundTc != null) {
			foundTc.addExceptionHandlers(tryCatch.getExceptionHandlers());
		} else {
			tryCatches.add(tryCatch);
		}
	}

	/**
	 * Generates the Instructions code, as Dalvik bytecode, in the codeItemCode buffer, as well as its
	 * header. It uses <i>symbolic</i> references, so must be parsed again later to link them to the "real"
	 * elements.
	 * Also fills the debug_code_item in this instance. Note however that the debug_info_offset field
	 * in the code_item header is not set, because we don't know where the debug_info_item is encoded
	 * for now. Note that the debug_info_item is not written here, only built.
	 * The alignment is not managed here, but must be by the calling method.
	 */
	public void generateCodeItemCode() {
		codeItemCode = new ByteVector();

		List<LocalVariable> localVariables = method.getLocalVariables();

		// If the method has no instruction (can it happen in non-generated code ?), we skip all the
		// coding of the instructions and the debug info.
		boolean mustEncodeMethod = (instructions != null) && (instructions.size() > 0); 

		// Initializes the debug_info_item by writing its header.
		if (mustEncodeMethod) {
			debugInfoItem.initializeDebugInfoItem(method.getParameters(), this, localVariables);
		}

		// Generates the header of the Code Item.
		codeItemCode.putShort(getRegistersSize());
		codeItemCode.putShort(incomingArgumentsSizeInWord);
		codeItemCode.putShort(outgoingArgumentsSizeInWord);
		codeItemCode.putShort(getTriesSize());
		codeItemCode.putInt(0); // The Debug Info Offset is not defined for now, as we don't know its offset yet.
		int sizeWord = size / 2;
		codeItemCode.putInt(sizeWord);

		int offsetByteCode = codeItemCode.getLength();

		if (mustEncodeMethod) {
			for (Instruction instruction : instructions) {
				debugInfoItem.parseDebugInformation(instruction, codeItemCode.getLength() - offsetByteCode);
				instruction.write(codeItemCode, constantPool);
			}

			// Padding may be needed if Try/Catch structures are present.
			if ((getTriesSize() != 0) && ((sizeWord % 2) != 0)) {
				codeItemCode.putShort(0);
			}

			// Encodes the Try/Catch structures in the second ByteVector.
			writeTryCatches(constantPool);

			// Ends the debug_info_item.
			debugInfoItem.closeDebugInfoItem();
		}
	}

	/**
	 * Replaces one Instruction with a new given one.
	 * @param oldInsn the replaced Instruction.
	 * @param newInsn the new Instruction.
	 */
	public void replaceInstructions(Instruction oldInsn, Instruction newInsn) {
		int indexInsnToReplace = instructions.indexOf(oldInsn);
		if (indexInsnToReplace >= 0) {
			instructions.remove(indexInsnToReplace);
			instructions.add(indexInsnToReplace, newInsn);

			// Updates the size of the Instructions.
			size += newInsn.getSize() - oldInsn.getSize();
		}
	}

	/**
	 * Parses the debug bytecode and maps the resolved indexes (Strings, Types) from the
	 * symbolic ones. The mapping between the two, done through two tables in the Constant Pool,
	 * must have been performed before.
	 * @param in input buffer to parse.
	 * @param offsetInputBuffer offset inside the input buffer from where to start the parsing.
	 * @return the parsed debug bytecode.
	 */
	public ByteVector mapResolvedIndexesForDebug(ByteVector in, int offsetInputBuffer) {
		return debugInfoItem.mapResolvedIndexes(in, offsetInputBuffer);
	}

	/**
	 * Parses the bytecode and tryCatch and maps the resolved indexes (Strings, Fields, Types, Methods) from
	 * the symbolic ones. The mapping between the two, done through four tables in the Constant Pool,
	 * must have been performed before.
	 * This method must be called when the method has its own bytecode i.e. when the "ConstantPool"
	 * optimization isn't performed.
	 */
	public void mapResolvedIndexes() {
		mapResolvedIndexesByteCode(codeItemCode, 0);	// Maps the bytecode indexes.
		codeItemTryCatch = mapResolvedIndexesTryCatch(codeItemTryCatch, 0, getTriesSize());		// Maps the try/catch indexes.
	}

	final static private byte INDEX_STRING = 1;
	final static private byte INDEX_FIELD = 2;
	final static private byte INDEX_TYPE = 3;
	final static private byte INDEX_METHOD = 4;

	/**
	 * Look-up table containing the Types of index every instructions uses. Includes 256 opcodes.
	 * 0 = no index.
	 */
	final private static byte[] typeOfIndexInInstructions = new byte[] {
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0x00
		0,0,0,0,0,0,0,0,0,0,INDEX_STRING,INDEX_STRING,INDEX_TYPE,0,0,INDEX_TYPE,		// 0x10
		INDEX_TYPE,0,INDEX_TYPE,INDEX_TYPE,INDEX_TYPE,INDEX_TYPE,0,0,0,0,0,0,0,0,0,0,		// 0x20
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0x30
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0x40
		0,0,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,
		INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,		// 0x50
		INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,
		INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_FIELD,INDEX_METHOD,INDEX_METHOD,		// 0x60
		INDEX_METHOD,INDEX_METHOD,INDEX_METHOD,0,INDEX_METHOD,INDEX_METHOD,INDEX_METHOD,INDEX_METHOD,INDEX_METHOD,0,0,0,0,0,0,0,		// 0x70
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0x80
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0x90
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0xa0
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0xb0
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0xc0
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0xd0
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		// 0xe0
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0			// 0xf0
	};

	/**
	 * Parses the bytecode and maps the resolved indexes (Strings, Fields, Types, Methods) from the
	 * symbolic ones. The given bytecode is <i>modified</i>.  
	 * The mapping between the two, done through four tables in the Constant Pool,
	 * must have been performed before.
	 * @param out ByteVector to be read and modified.
	 * @param offsetByteCode offset in bytes where the parsing must begin.
	 */
	public void mapResolvedIndexesByteCode(ByteVector out, int offsetByteCode) {
		// The same byte array is shared by the reader and writer.
		IDalvikValueReader reader = new DalvikValueReader(out.getBuffer());

		// Looks for the size of the instructions. We don't want to parse the possible try/catch
		// structures after the code.
		reader.seek(INSNS_SIZE_OFFSET + offsetByteCode);
		int insnsSizeInBytes = reader.uint() * 2; // The read size is in words, hence the * 2.
		int endPos = insnsSizeInBytes + reader.getPos();
		int indexFoundType = 0;

		// Parses all the opcodes.
		while (reader.getPos() < endPos) {
			int opcode = reader.ubyte();
			int secondByte = reader.ubyte();
			int indexSize = 2;		// By default, we read short index, but it can raise to 4.

			if (opcode == Opcodes.INSN_NOP) {
				// If we find an array, we have to skip it.
				int bytesToSkip = 0;
				switch (secondByte) {
				case 0x1: { // Packed-switch format.
					int size = reader.ushort();
					reader.skipInt();  // Skip first_key.
					bytesToSkip = size * 4;
					break;
				}
				case 0x2: { // Sparse-switch format.
					int size = reader.ushort();
					bytesToSkip = size * 4 * 2;
					break;
				}
				case 0x3: { // Fill-array-data format.
					int width = reader.ushort();
					int size = reader.uint();
					bytesToSkip = (size * width + 1) / 2 * 2;
					break;
				}
				}
				if (bytesToSkip > 0) {
					reader.relativeSeek(bytesToSkip);
				}
			} else {
				// Searches the type of the index inside a look-up table.
				indexFoundType = typeOfIndexInInstructions[opcode];
				if (opcode == Opcodes.INSN_CONST_STRING_JUMBO) {
					indexSize = 4;
				}

				// If we didn't find any index, then skip to the next Instruction.
				if (indexFoundType == 0) {
					// Skips the instruction according to its size, - 2 because we already read the 16-bit opcode.
					int instructionSize = Instruction.getInstructionSizeInByte(opcode) - 2;
					if (instructionSize > 0) {
						reader.relativeSeek(instructionSize);
					}
				} else {
					int savedReaderOffset = reader.getPos();
					// We get to the encoded index.
					int indexOffset = savedReaderOffset; // + 2 - 2 : +2 to reach the index, -2 because we already read the 16-bit opcode.
					reader.seek(indexOffset);
					// Reads the symbolic index according to its size.
					int symbolicIndex = (indexSize == 2 ? reader.ushort() :
						reader.ushort() + (reader.ushort() << 16));

					int resolvedIndex = -1;
					switch (indexFoundType) {
					case INDEX_STRING:
						resolvedIndex = constantPool.getResolvedStringIndexFromSymbolicStringIndex(symbolicIndex);
						break;
					case INDEX_FIELD:
						resolvedIndex = constantPool.getResolvedFieldIndexFromSymbolicFieldIndex(symbolicIndex);
						break;
					case INDEX_TYPE:
						resolvedIndex = constantPool.getResolvedTypeIndexFromSymbolicTypeIndex(symbolicIndex);
						break;
					case INDEX_METHOD:
						resolvedIndex = constantPool.getResolvedMethodIndexFromSymbolicMethodIndex(symbolicIndex);
						break;
					default:
						try { throw new Exception("Unknown Index type.");
						} catch (Exception e) { e.printStackTrace(); }
					}

					// Overwrites the symbolic index with the resolved index.
					if (indexSize == 2) {
						out.putShort(resolvedIndex, indexOffset);
					} else {
						out.putShort(resolvedIndex & 0xffff, indexOffset);
						out.putShort((resolvedIndex >> 16) & 0xffff, indexOffset + 2);
					}

					// Skips the instruction according to its size, - 2 because we already read the 16-bit opcode.
					int instructionSize = Instruction.getInstructionSizeInByte(opcode) - 2;
					if (instructionSize > 0) {
						reader.seek(savedReaderOffset + instructionSize);
					}
				}
			}
		}
	}

	/**
	 * Parses the tryCatch and maps the resolved indexes (Strings, Fields, Types, Methods) from the
	 * symbolic ones. The input vector is not modified. 
	 * The mapping between the two, done through four tables in the Constant Pool,
	 * must have been performed before.
	 * @param in a ByteVector containing the try_catch bytecode to modify.
	 * @param offsetInInputArray offset in bytes where the parsing must begin.
	 * @param nbTries count of Try/Catch.
	 * @return a ByteVector containing the try_catch bytecode.
	 */
	public ByteVector mapResolvedIndexesTryCatch(ByteVector in, int offsetInInputArray, int nbTries) {
		ByteVector out = new ByteVector();

		if (nbTries == 0) {
			return out;
		}
		// The reader and writer are not shared, as ULeb128 encoded field may differ in size.
		IDalvikValueReader reader = new DalvikValueReader(in.getBuffer());

		reader.seek(offsetInInputArray);

		// Duplicates the try_item elements.
		for (int i = 0; i < nbTries; i++) {
			out.putInt(reader.uint());
			out.putShort(reader.ushort());
			out.putShort(reader.ushort()); // It may move will be patched later.
		}

		// Get reference position of the encoded_catch_handler
		int absoluteOldPos = reader.getPos();
		int absoluteNewPos = out.getLength();
		HashMap<Integer,Integer> oldToNewHandlerOffsets = new HashMap<Integer,Integer>();
		// Encodes the encoded_catch_handler elements. First the list.
		int nbEncodedCatchHandler = reader.uleb128();
		out.putUleb128(nbEncodedCatchHandler);
		// Encodes each of them.
		for (int i = 0; i < nbEncodedCatchHandler; i++) {
			// Detects shifts in handler position
			int oldOffset = reader.getPos() - absoluteOldPos;
			int newOffset = out.getLength() - absoluteNewPos;
			if (oldOffset != newOffset) {
				oldToNewHandlerOffsets.put(oldOffset, newOffset);
			}

			int readSize = (int)reader.sleb128(); // May be negative if catch_all present.
			int size = (readSize >= 0 ? readSize : -readSize);
			out.putSleb128(readSize);
			// Encodes each encoded_type_addr_pair.
			for (int j = 0; j < size; j++) {
				int symbolicIndex = reader.uleb128(); // Encodes the type_idx.
				int resolvedIndex = constantPool.getResolvedTypeIndexFromSymbolicTypeIndex(symbolicIndex);
				out.putUleb128(resolvedIndex);

				out.putUleb128(reader.uleb128());	// Encodes the addr.
			}

			if (readSize <= 0) {
				out.putUleb128(reader.uleb128());	// Encodes the catch_all_addr.
			}
		}

		if (oldToNewHandlerOffsets.size() != 0) {
			// Handlers were shifted
			int offset = HANDLER_OFFSET_IN_TRY_ITEM_STRUCTURE;
			for(int i = 0; i < nbTries; i++) {
				reader.seek(offsetInInputArray + offset);
				int oldHandlerOffset = reader.ushort();
				Integer newHandlerOffset = oldToNewHandlerOffsets.get(oldHandlerOffset);
				if (newHandlerOffset != null) out.putShort(newHandlerOffset, offset);
				offset += TRY_ITEM_STRUCTURE_SIZE;
			}
		} 
		return out;
	}


	// ------------------------------------
	// Getters and setters.
	// ------------------------------------

	/**
	 * Returns the size in bytes of all the instructions of this item (and by extension, the method).
	 * Does NOT take in account the header of the Code Item.
	 * @return the size in bytes of all the instructions of this item, without the header.
	 */
	public int getSize() {
		return size;
	}

	/**
	 * Returns the number of Try structure in this Code Item.
	 * @return the number of Try structure in this Code Item.
	 */
	public int getTriesSize() {
		return tryCatches.size();
	}

	/**
	 * Sets the Register Size of this code.
	 * @param registerSize the Register Size of this code.
	 */
	public void setRegisterSize(int registerSize) {
		this.registerSize = registerSize;
	}

	/**
	 * Gets the number of registers used by this code.
	 * @return the number of registers used by this code.
	 */
	public int getRegistersSize() {
		return registerSize;
	}

	/**
	 * Returns the list of the Labels of this code item.
	 * @return the list of the Labels of this code item.
	 */
	public List<Label> getLabels() {
		return labels;
	}

	/**
	 * Gets the size in word of the Incoming Arguments.
	 * @return the size in word of the Incoming Arguments.
	 */
	public int getIncomingArgumentsSizeInWord() {
		return incomingArgumentsSizeInWord;
	}

	/**
	 * Sets the size in word of the Incoming Arguments.
	 * @param incomingArgumentsSizeInWord the size in word of the Incoming Arguments. 
	 */
	public void setIncomingArgumentsSizeInWord(int incomingArgumentsSizeInWord) {
		this.incomingArgumentsSizeInWord = incomingArgumentsSizeInWord;
	}

	/**
	 * Gets the size in word of the Outgoing Arguments.
	 * @return the size in word of the Outgoing Arguments.
	 */
	public int getOutgoingArgumentsSizeInWord() {
		return outgoingArgumentsSizeInWord;
	}

	/**
	 * Sets the size in word of the Outgoing Arguments.
	 * @param outgoingArgumentsSizeInWord the size in word of the Outgoing Arguments.
	 */
	public void setOutgoingArgumentsSizeInWord(int outgoingArgumentsSizeInWord) {
		this.outgoingArgumentsSizeInWord = outgoingArgumentsSizeInWord;
	}

	/**
	 * Returns the offset of the Code Item from the beginning of the file. It is only known when
	 * producing the output file, after the input has been fully parsed.
	 * @return the offset of the Code Item from the beginning of the file.
	 */
	public int getOffset() {
		return offset;
	}

	/**
	 * Sets the offset of the Code Item from the beginning of the file.
	 * @param offset the offset of the Code Item from the beginning of the file.
	 */
	public void setOffset(int offset) {
		this.offset = offset;
	}

	/**
	 * Sets the Method this Code Item is linked to.
	 * @param method the Method this Code Item is linked to.
	 */
	public void setMethod(Method method) {
		this.method = method;
	}

	/**
	 * Sets the offset of the Debug Info Item inside the already encoded Code Item.
	 * @param out Vector of the Dex file. It already contains the current Code Item. 
	 * @param debugInfoItemOffset the offset of the debug_info_item, encoded or soon to be.
	 */
	public void setDebugInfoItemOffset(ByteVector out, int debugInfoItemOffset) {
		out.putInt(debugInfoItemOffset, offset + DEBUG_INFO_OFFSET_OFFSET); // Reaches the debug_info_off field.
	}

	/**
	 * Sets the first line number of this code_item, but only if none has been found before.
	 * @param firstLineNumber the first line number of this code_item.
	 */
	public void setFirstLineNumber(int firstLineNumber) {
		if (this.firstLineNumber == 0) {
			this.firstLineNumber = firstLineNumber;
		}
	}

	/**
	 * Returns the first line number found. It may be 0 if none were.
	 * @return the first line number found, or 0.
	 */
	public int getFirstLineNumber() {
		return firstLineNumber;
	}

	/**
	 * Returns the code_item code (including the code_item header and the bytecode), <i>without</i> the
	 * try/catch fields after the insns field, but with the padding if needed.
	 * It uses <i>symbolic</i> references, so must be parsed again to link them to the "real"
	 * elements.
	 * @return the code_item code, without the try/catch fields.
	 */
	public ByteVector getCodeItemCode() {
		if (codeItemCode == null) {
			generateCodeItemCode();
		}
		return codeItemCode;
	}

	/**
	 * Returns the try/catch section of the code_item, beginning by the possible padding after the insns field,
	 * or Null if no try/catch is present.
	 * It uses <i>symbolic</i> references, so must be parsed again to link them to the "real"
	 * elements.
	 * @return the try/catch code, or Null.
	 */
	public ByteVector getCodeItemTryCatch() {
		if (codeItemCode == null) { // Generating the code will generate the try/catch
			generateCodeItemCode(); // (or not, if there is none).
		}
		return codeItemTryCatch;
	}

	/**
	 * Returns the debug_info_item code, still using symbolic indexes.
	 * It is available only after this method bytecode has been generated.
	 * @return the debug_info_item code still using symbolic indexes.
	 */
	public ByteVector getDebugInfoItemCode() {
		return debugInfoItem.getDebugInfoItemCode();
	}

	/**
	 * Indicates if at least one index is encoded in the debug code. We use it to optimize the mapping
	 * with the resolved indexes : it doesn't need do be done if no index was found.
	 * @return true if at least one index is encoded in the debug code.
	 */
	public boolean areSymbolicIndexesUsedInDebugCodeItem() {
		return debugInfoItem.areSymbolicIndexesUsed();
	}


	// ------------------------------------
	// Private methods.
	// ------------------------------------

	/**
	 * Encodes the Try/Catch/Handler structures of the code_item, inside a new codeItemTryCatch buffer.
	 * @param constantPool the Constant Pool.
	 */
	private void writeTryCatches(ConstantPool constantPool) {
		if (getTriesSize() == 0) {
			return;
		}
		codeItemTryCatch = new ByteVector();

		// Encodes the "tries" field. For now, the handler is not encoded.
		for (TryCatch tc : tryCatches) {
			int startAddr = tc.getStart().getOffset();
			int endAddr = tc.getEnd().getOffset();
			codeItemTryCatch.putInt(startAddr / 2); // / 2 because based on 16-bit units.
			codeItemTryCatch.putShort((endAddr - startAddr) / 2); // / 2 because based on 16-bit units.
			codeItemTryCatch.putShort(0); // Not encoded for now.

			// Builds an EncodedCatchHandler to provide uniqueness of these structures, and link it to
			// the TryCatch.
			EncodedCatchHandler ech = new EncodedCatchHandler(constantPool);
			for (ExceptionHandler eh : tc.getExceptionHandlers()) {
				ech.addTypeAddrPair(eh.getType(), eh.getHandler());
			}
			encodedCatchHandlers.add(ech);

			// Links the TryCatch to the EncodedCatchHandler.
			tryCatchToEncodedCatchHandler.put(tc, ech);
		}

		// Encodes the encoded_catch_handler_list.
		int absoluteOffsetEncodedCatchHandlerList = codeItemTryCatch.getLength();
		// Encodes total count of Catch Handlers to be encoded. They may be shared.
		codeItemTryCatch.putUleb128(encodedCatchHandlers.size());

		for (EncodedCatchHandler ech : encodedCatchHandlers) {

			int offsetEncodedCatchHandlerCurrentItem = codeItemTryCatch.getLength() - absoluteOffsetEncodedCatchHandlerList;
			encodedCatchHandlerToRelativeOffset.put(ech, offsetEncodedCatchHandlerCurrentItem);
			ech.write(codeItemTryCatch);
		}

		// Links the TryCatches with the EncodedCatchHandler offsets.
		int offset = HANDLER_OFFSET_IN_TRY_ITEM_STRUCTURE;
		for (TryCatch tc : tryCatches) {
			codeItemTryCatch.putShort(encodedCatchHandlerToRelativeOffset.get(tryCatchToEncodedCatchHandler.get(tc)), offset);
			offset += TRY_ITEM_STRUCTURE_SIZE;
		}
	}
}
